/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/pgl.h>
#include <pgl/core/PGLLegend.h>
#include <pgl/core/PGLRegion.h>
#include <pgl/core/PGLObjectManager.h>
#include <pgl/core/PGLPools.h>
#include <pgl/core/PGLRegion.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

PGL_IMPLEMENT_DYNCREATE(CPGLLegend,CPGLPave);

#ifdef _DEBUG
void CPGLLegend::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLPave::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("--- CPGLLegend ID ") << GetID() <<_T(" ---\n");
}

void CPGLLegend::AssertValid() const
{
    // call inherited AssertValid first
    CPGLPave::AssertValid();
} 
#endif

void CPGLLegend::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLLegend");

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// Adding base class
			CPGLPave::SerializeXML(ar);

			xml.AddChildElem(_T("TextStyle"));
			xml.IntoElem();
				m_text.SerializeXML(ar);
			xml.OutOfElem();
			
			xml.AddChildElemEx(_T("MarkerWidth"), m_dMarkerWidth);
			xml.AddChildElemEx(_T("IDs"),m_lIDs);
		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;



		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// loading base class
			CPGLPave::SerializeXML(ar);

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("TextStyle")))
			{
				xml.IntoElem();
					m_text.SerializeXML(ar);
				xml.OutOfElem();
			}

			xml.FindGetChildData(_T("MarkerWidth"), m_dMarkerWidth);
			xml.FindGetChildData(_T("IDs"),m_lIDs);

		xml.OutOfElem();

		PostUpdateExtent();
	}

}

CPGLLegend::CPGLLegend()
: m_dMarkerWidth(20)
{
	using namespace gfxinterface;

	SetName(_T("Legend"));
	SetBorderType(CPGLPave::BorderLowerRightShadow);
	SetSolidFillColor(1,1,1,1);
	SetDashPattern(_T(""));
	m_text.SetString(_T("Legend Style"));
	m_text.SetAlign(TextAlignmentLeft);

	SetInsideBorderSize(CPGLRectD(5,5,5,5));
	GetArea().SetAnchorType(CPGLArea2D::AnchorUpperLeft);

	SetPosition( LegendPosUpperRight );
}

CPGLLegend::~CPGLLegend()
{
	Clear();
}

CPGLLegend& CPGLLegend::operator=(const CPGLLegend& lg)
{
	if (this != &lg)
	{
		this->CPGLPave::operator=(lg);

		m_dMarkerWidth = lg.m_dMarkerWidth;
		m_text = lg.m_text;
		m_lIDs = lg.m_lIDs;
	}
	return *this;
}

void CPGLLegend::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	// testing if legend visible
	if (!IsVisible())
		return;
	// testing if in layer
	if (!IsInLayer(gfx.GetLayer()))
		return;

	// make sure there is elements in the list
	CPGLObjectStPtr pObject;
	bool bAnyObject=false;
	CUINTList::const_iterator iID;
	for (iID = m_lIDs.begin(); iID != m_lIDs.end() && !bAnyObject; iID++)
	{
		pObject= CPGLObjectPoolSingleton::Find(*iID);

		if (pObject && pObject->IsVisible())
			bAnyObject=true;
	}
	if (!bAnyObject)
		return;

	m_text.UpdateExtent(gfx);
	FitBox(gfx);

	// drawing corners
	CPGLPave::PlotGfx(gfx);

	CPGLRectD rWIn,rMarker,rMarkerClip, rWorldClip;

	// getting inside bounding box
	rWIn = GetInsideWorldBBox(gfx);

	// preparing coordinates
	rMarker.left=rWIn.left;
	rMarker.right=rMarker.left+ gfx.BBToWorldAbsx(m_dMarkerWidth);
	rMarker.top = rWIn.top;

	double hText = gfx.BBToWorldAbsy(m_text.GetPixelHeight());
	double dy = hText+gfx.BBToWorldAbsy(5);
	double xText = rMarker.right + gfx.BBToWorldAbsx(5);
	double yText = rMarker.top-hText-gfx.BBToWorldAbsy(5);

	rMarker.bottom=yText+gfx.BBToWorldAbsy(3);


	std::vector<gfxinterface::SPointD> vPoints(m_lIDs.size());
	std::vector<std::_tstring> vLegends(m_lIDs.size());

	// getting clip world
	rWorldClip.left= gfx.GetWorldClipLeft();
	rWorldClip.right=gfx.GetWorldClipRight();
	rWorldClip.top=gfx.GetWorldClipTop();
	rWorldClip.bottom=gfx.GetWorldClipBottom();

	// adding objects
	size_t i;
	for (iID = m_lIDs.begin(), i=0; iID != m_lIDs.end(); ++iID)
	{
		pObject = CPGLObjectPoolSingleton::Find(*iID);
		if (pObject && pObject->IsVisible())
		{
			ASSERT_VALID(pObject.get());

			vPoints[i].x=xText;
			vPoints[i].y=yText;
			vLegends[i]=pObject->GetLegendString();		

			// intersecting with clipping region...
			rMarkerClip.Intersect(rMarker, rWorldClip, false);
			if (!rMarkerClip.IsEmpty())
			{
				gfx.PushClipWorld(rMarkerClip.left,rMarkerClip.bottom,rMarkerClip.right,rMarkerClip.top);
				pObject->PlotLegendGfx(gfx, rMarkerClip);
				gfx.PopClipWorld();
			}

			yText-=dy ;
			rMarker.top-=dy;
			rMarker.bottom-=dy;

			i++;
		}
	}
	vPoints.resize(i);
	vLegends.resize(i);

	m_text.CPGLObject::PlotGfx(gfx);
	m_text.GetFont().SetFontGfx(gfx);
	gfx.DrawTextStrip(vPoints, vLegends, gfxinterface::TextAlignmentLeft);
}

void CPGLLegend::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	CPGLPave::UpdateExtent(gfx);
	m_text.UpdateExtent(gfx);
}

void CPGLLegend::FitBox(gfxinterface::CGfxInterface &gfx)
{
	// preparing coordinates
	double dy = 5;
	double hText = 0;
	double wText = 0;

	// adding objects
	CString legend;
	CPGLObjectStPtr pObject;
	CUINTList::const_iterator iID;
	UINT i;
	for (iID = m_lIDs.begin(), i=0; iID != m_lIDs.end(); iID++)
	{
		pObject=CPGLObjectPoolSingleton::Find( *iID);
		if (pObject && pObject->IsVisible())
		{
			ASSERT_VALID(pObject.get());
			legend=pObject->GetLegendString();
			if(legend.IsEmpty())
				legend=_T("D");
			m_text.SetString(legend);
			m_text.UpdateExtent(gfx);
			
			wText = __max( wText, m_text.GetPixelWidth() );
			hText+=m_text.GetPixelHeight() + 5;
			i++;
		}
	}

	wText+= m_dMarkerWidth + 10;

	// updating normbox...
	GrowInsideBBAbsToOutsideNormBBox(gfx, wText, hText);
}

void CPGLLegend::SetPosition(ELegendPos pos)
{
	using namespace gfxinterface;

	static CPGLRectD rBBox(0,0,1,1);
	static SPointD point;
	
	switch(pos)
	{
	case LegendPosLowerLeft:
		GetArea().SetAnchorType(CPGLArea2D::AnchorLowerLeft);
		GetArea().SetArea(rBBox, CPGLArea2D::CoordNormClip);
		point.x=10;
		point.y=10;
		GetArea().SetBBTrans(point);
		break;
	case LegendPosUpperLeft:
		GetArea().SetAnchorType(CPGLArea2D::AnchorUpperLeft);
		GetArea().SetArea(rBBox, CPGLArea2D::CoordNormClip);
		point.x=10;
		point.y=-10;
		GetArea().SetBBTrans(point);
		break;
	case LegendPosLowerRight:
		GetArea().SetAnchorType(CPGLArea2D::AnchorLowerRight);
		GetArea().SetArea(rBBox, CPGLArea2D::CoordNormClip);
		point.x=-10;
		point.y=10;
		GetArea().SetBBTrans(point);
		break;
	case LegendPosUpperRight:
		GetArea().SetAnchorType(CPGLArea2D::AnchorUpperRight);
		GetArea().SetArea(rBBox, CPGLArea2D::CoordNormClip);
		point.x=-10;
		point.y=-10;
		GetArea().SetBBTrans(point);
		break;
	}
}

void CPGLLegend::PushTop(CPGLObjectPtr pObject)		
{	
	if(!pObject)
		return;

	m_lIDs.push_front(pObject->GetID());	
	PostUpdateExtent();
};

void CPGLLegend::PushBottom(CPGLObjectPtr pObject)
{	
	if (!pObject)
		return;
	m_lIDs.push_back(pObject->GetID()); 
	PostUpdateExtent();
};
